# encoding=utf-8

"""Application config
    - 工程代码配置表
    部署的时候需要修改的几个地方：
     1. FRP_PATH: frp流量转发程序目录(如果不使用invoke的启动命令到也不用填)
     2. DEBUGGER: 要改成False, 否则可能出现框架报错，程序停住
     3. 如果修改了Redis默认参数，需要在SystemConfig.Redis进行配置
     4. BASE_URL: 需要改成部署的服务器IP以及Port(用于告诉c端资源的访问路径)


     readme.md:
        > 这里留下代码的编写意见以及坑点，tacom: 2022.9.9(个人预计11.06离开)

        # 1. 开发方式
            代码的开发方式整体采用敏捷开发的方式（就是那个6个月后项目重写的方式），导致大量文档缺失，
        并且本人是编外人员，一旦离开基本没有可能再去带新人。

        ## 2. 技术坑点
        ### 2.1 效率加速
            由于代码效率的影响，中间需要加入很多效率优化技术，预计使用的技术框架如下：cxx, cuda,
        ispc, intel TDD, OpenMP等，效率加速的必然会造成代码的可读性下降（主要还要求保留旧代码),
        同时需要把相关代码利用python的c接口来执行(混编)，中间无法进行单行调试，需要比较好的代码测试能力，
        以及bug定位处理能力。
        ### 2.2 python web
            python web的信息处理能力不如Java，这导致了一些莫名奇妙的问题，比如链接中断还不报错，
        超过最大链接数后直接框架报错，整个程序停止啥问题，需要一定的web排错能力。

        ## 3. 代码坑点
        ### 3.1 web接口
            问题比较大的就是图片上传接口，需要对前后台的网络功能进行重写，现在修了一部分，发现偶尔
        有点图片上传了，但是服务器还是空的，还不报错，真奇怪。同时前后台如果不进行接口限流的话，
        容易框架崩溃。
        ### 3.2 代码组织
            业务逻辑的部分遵从MVC设计模式，但是需求太多，部分代码就没去写继承体系，没有抽象出整体的
        常量配置表，比如本系统的批处理功能，由于批处理需要全体图片大小一致（当初还有500几的和384x384混用）
        由于需求变换，然后很多地方也没写配置表，直接(384,384)丢上去了，地方有点多，如果要排错可能比较麻烦。
        ### 3.3 加速部分
            毕竟是cxx编写还是混编的，同时需要在编译的时候加入不同平台的信息，所以每个平台都要手动编译测试
        挺麻烦的，除非当初所有人的代码通过cxx进行编写。
        ### 3.4 版本问题
            这个代码本人接手的时候就是一堆错误，除了seg_开头的，其余代码是陈年代码，导致越往后代码越运行
        不起来，如果跑步起来，那我只能说兄弟加油吧。
        ### 3.5 算法处理主流程
            说是性能强大的orin, 但可以让两个请求进行预测试试，卡的一比，所以代码进行了线程池限制，数量为1，
        如果后续开发人员有能力的话，可以改改。

        ## 4. 代码编写建议
        ### 4.1 匹配需求
            需求反应的技术结构都差不多，可以看看进度追踪表，看看是否有类似的功能实现，有的话就先去看代码
        然后如果有继承体系，就对着抄。如果没有就自己抽象，注意**不要动源代码**，根据设计模式，应该对扩展
        开放，对修改关闭。
        ### 4.2 常量增加配置表
            所说的配置表就是下面的常量存储类，本人在需求中大量遇到了随时切换的场景，所以对需求进行抽象，
        也是提高工作效率的一个方案。
        ### 4.3 加速方案选择
            OpenMP: 删除编译器对变量是否冲突的判断之类的，提高运算速度
            ispc: 用更高性能的指令提高运算速度
            Intel TDD: 尽可能提高CPU核心利用率
            CUDA: 和TDD类似
            选择的时候，如果不能将问题转换为归并、矩阵问题，就不要使用CUDA,通常负优化。

        ## 5. 系统目标
            这是一个算法后台，实现算法功能后，就会像一个无脑处理内核存在。所以这套代码一旦完成，添加分布式
        模块以后，就可以介入nacos等待任务调度了。剩下的调度部分应该使用springboot,把用户的信息进行存储，
        对用户的请求，读取每台分布式算法处理机的负载进行调度，然后前端只应该访问Java的后台，这应该会提高不少
        稳定程度以及系统整体的感受。
"""
import traceback
from datetime import datetime

import torch
import os


class ApplicationConfig:
    class SystemConfig:
        """ ---------- for nohup -------------"""
        # invoke 快速启动代理用的，可以不填写，但注意不要使用相关invoke
        FRP_PATH = ""
        FRP_EXEC = "frpc"
        FRP_CONFIG = "frpc.ini"
        FRP_LOG = "nohup.log"
        """ ---------- end nohup -------------"""

        """ ---------- for flask -------------"""
        # flask 框架整体配置表, 用于快速切换
        DEBUGGER = True
        SERVER_HOST = "0.0.0.0"
        SERVER_PORT = 50001
        SERVER_MODE = 'PROCESSES'  # 这两种方式并不能实现高并发，推荐进程，稳定flask不太稳定的情况，高并发请研究协程
        SERVER_MODE_DICT = {
            'NORMAL': {
                'port': SERVER_PORT,
                'host': SERVER_HOST,
                'debug': DEBUGGER
            },
            'PROCESSES': {
                'port': SERVER_PORT,
                'host': SERVER_HOST,
                'debug': DEBUGGER,
                'processes': True
            },
            'THREADED': {
                'port': SERVER_PORT,
                'host': SERVER_HOST,
                'debug': DEBUGGER,
                'threaded': True
            }
        }
        """ ---------- end flask -------------"""

        """ ---------- for batch -------------"""
        #####################################
        # 这个部分的数据要求根据部署机器的性能自行调参
        #####################################

        NUM_PREDICT_USER = 1  # controller, 控制批量化的执行执行对象数量



        # 整体的运算模式
        MODE = "FOLDER"  # 按照文件夹处理图片，整体上这个快点
        MODE_FOLDER = "FOLDER"
        MODE_PICTURE = "PICTURE"  # 没空实现

        # 线程调参
        NUM_WORKER = 4  # 常规线程池数量


        ################# 兼容性，已经重写成批量版本, 看vascular #########
        VASCULAR_NUM_WORKER = 6  # Vascular整体使用的线程数量
        VASCULAR_USE_WORKER = True  # Vascular内部是否开启多线程加速
        #############################################################

        # 批量调参
        BATCH_SIZE = 8  # 旧常规批量
        BATCH_SIZE_NORMAL = 4  # 常规批量
        BATCH_SIZE_ENHANCE = 4  # 增强很占内存
        BATCH_SIZE_SEGMENTATION_NERVEFORMER = 8  # nerveformer

        ############################################################
        # 这里的代码还没添加到项目中，调试batch调小是显卡内存有限
        # SERVCIE代码ORIN中的配置，按照实际测试中跑出来的，代码可能有内存泄漏
        # 超过12基本上每跑一轮涨点，需要处理
        #############################################################
        BATCH_MODE = "DEBUGGER"
        BATCH_DICT = {
            "DEBUGGER": {
                "BATCH_SIZE": 4,
                "BATCH_SIZE_NORMAL": 2,
                "BATCH_SIZE_ENHANCE": 2,
                "BATCH_SIZE_SEGMENTATION_NERVEFORMER": 4
            },
            "SERVICE": {
                "BATCH_SIZE": 12,
                "BATCH_SIZE_NORMAL": 12,
                "BATCH_SIZE_ENHANCE": 12,
                "BATCH_SIZE_SEGMENTATION_NERVEFORMER": 12
            }
        }


        # 开启CUDA CV
        # 这个cuda_cv由于技术原因，主要用了c++, cuda混编的方式提速, 详见seg_cuda/cvcudaImpl
        # 或者说python/c++/ipsc/cuda哪个快，哪个能减轻cpu负载，提高整体运行效率
        # 如果是False的话，将会全程运行python代码
        USER_CUDA_CV = True
        CUDA_CV_MAX_EDGE = 8192  # cuda的硬件极限，处理8192 x 8192

        """ ---------- end batch -------------"""

        """ ---------- for pytorch -------------"""
        DEVICE = torch.device('cuda:0' if torch.cuda.is_available() else 'cpu')
        DEVICE_NAME = 'cuda:0'
        """ ---------- end pytorch -------------"""

        """ ---------- for message middle -------------"""
        # 用于记录代码执行进度，不好用舍弃
        # CELERY_BROKER_URL = 'redis://localhost:6379/0'  # 配置消息中间件
        # CELERY_RESULT_BACKEND = 'redis://localhost:6379/0'  # 配置消息存储位置
        # celery = Celery('segmentation', broker=CELERY_BROKER_URL, backend=CELERY_RESULT_BACKEND)

        REDIS_CONFIG = {
            'host': 'localhost',
            'port': 6379,
            'password': '',
            'db': 0
        }

        REDIS_TASK_QUEUE_PRE = "ARCUS_TASK_QUEUE_PRE"  # 组合rediskey作为任务队列的区分标志
        REDIS_PREDICT_QUEUE_PRE = "ARCUS_PREDICT_QUEUE_PRE"  # 组合rediskey作为predict主流程信息的区分标志


        """ ---------- end message middle -------------"""

        """ ---------- for some chinese ------"""
        # 代码中用到的中文部分快速切换, (新的batch处理中没有写相关实现), 看vascular
        USE_MATPLOTLIB_CHINESE = False  # 使用中文的兼容性差点
        USE_MATPLOTLIB_TXT = True  # 存储直方图信息到文件
        """ ---------- end for chinese -------"""

        """ ---------- for vascular ------"""
        VASCULAR_DELETE_STRIDE = 1  # 输入图很可能有白边，需要删除几个像素
        # 根据测试线程数在int(batch * 0.6)最好，但是需要节约资源，一般int(batch * 0.4/0.3)
        # stride只能调大，15是384中测试比较稳定的一个较小值
        VASCULAR_BATCH, VASCULAR_STRIDE, VASCULAR_THREAD = 8, 15, 3
        VASCULAR_EACH_SHAPE = (384, 384)  # 本系统按照批量处理设计，以后都要统一大小
        VASCULAR_ACTION = "FIRST"  # 行为选择，根据马老师意愿保留多个选项，通常得按照python/cxx方式切换，这里按照执行效率优化顺序
        VASCULAR_PROCESS_CONFIG = {
            "CNFD": {
                "FIRST": {"name": "CNFD", "use_cxx": False},
                "SECOND": {"name": "CNFD", "use_cxx": True}
            },
            "CNBD": {
                "FIRST": {"name": "CNBD", "use_cxx": True, "one_by_one": False},
                "SECOND": {"name": "CNBD", "use_cxx": False, "one_by_one": True}
            },
            "CTBD": {
                "FIRST": {"name": "CTBD", "use_cxx": True, "one_by_one": True},
                "SECOND": {"name": "CTBD", "use_cxx": False, "one_by_one": True}
            },
            "CNFL": {
                "FIRST": {"name": "CNFL", "use_cxx": False, "one_by_one": True},
                "SECOND": {"name": "CNFL", "use_cxx": False, "one_by_one": True}
            }
        }
        """ ---------- end vascular ------ """

        """ ---------- for grade    ------ """
        # 从grade算法开始，开发机显存不足，需要调参
        # 后续需要模型的算法，都会加入相关开关，并根据自身情况调整
        GRADE_USE_CUDA = True
        """ ---------- end grade    ------ """

    class URLConfig:
        # 用于拼接代码实现相关的文件返回等内容，非设定框架
        # BASE_URL = "192.168.31.200"  # Jetson develop board ip
        BASE_URL = "127.0.0.1"  # WuJie16 develop laptop ip
        BASE_PORT = "50001"
        SPRING_URL = "127.0.0.1"
        SPRING_PORT = "40001"

    class PathConfig:
        # 由于前期需求缺失，数据库没有设计，所以这里填写个人描述的用户文件映射表
        # 此类需要通过继承从而扩充常量
        # 我的资源定位目录
        SYSTEM_RESOURCE = os.path.join(os.path.dirname(__file__), 'resources')
        SYSTEM_LOG_FOLDER = "logs"
        SYSTEM_LOG_FILE = "Consolelog{}.txt".format(datetime.now().strftime("%Y_%m_%d_%H"))

        # 用于创建一个2h的临时目录(以下兼容旧代码)
        BASE_SAVE_PATH = "tmp/multi_batch_tmp"
        DEFAULT_USER_NAME = "tacom"
        DEFAULT_USER_TOKEN = "12345"

        # 用于各种临时资源存储
        USER_TMP_FOLDER = "user_tmp"

        """ ########## 角膜神经分割 ########## """
        NORMAL_SAVE_PATH = "normal"
        ENHANCE_SAVE_PATH = "Enhance"
        ORIGIN_ZERO_FOLDER = "0"  # 原始图片, 增强结果
        ORIGIN_ONE_FOLDER = "1"  # 角膜神经分割, 原/增强
        ORIGIN_ONE_SUB_FOLDER = "1_1"  # 存放细胞分割结果(文本,代码还没有)
        ORIGIN_TWO_FOLDER = "2"  # 弯曲度分级，原/增强 __图__
        ORIGIN_TWO_SUB_FOLDER = "2_1"  # 弯曲度分级，原/增强 __数值__
        ORIGIN_THREE_FOLDER = "3"  # 弯曲度热力投影，原/增强
        ORIGIN_NORMAL_TMP = "normal_tmp"  # 放原始临时图像的文件夹
        ORIGIN_ENHANCE_TMP = "enhance_tmp"  # 放增强临时图像的文件夹
        ORIGIN_NNUNET_TMP = "nnunet_tmp"  # 2023.4.19暂不加入路径组合的常量, 由于无法确认是否增强,所以直接不区分名称

        # 角膜神经量化
        VASCULAR_BASE_FOLDER = "vascular"
        VASCULAR_ZERO_FOLDER = "Color"  # 连通域
        VASCULAR_ONE_FOLDER = "Refinement"  # 细化，不返回，给后续步骤用
        VASCULAR_ONE_SUB_FOLDER = "Branch_point"  # 分支点
        VASCULAR_ONE_THIRD_FOLDER = "Segmentation"  # 分支点断开，不返回
        VASCULAR_TWO_FOLDER = "Degree"  # 弯曲度表格
        VASCULAR_THREE_FOLDER = "result"  # 四个需要返回的数值
        VASCULAR_TMP_ZERO_FOLDER = "Color_tmp"  # color结果合并到原图的存放位置
        VASCULAR_TMP_ONE_SUB_FOLDER = "Branch_point_tmp"  # branch_point结果合并到原图的存放位置
        VASCULAR_TMP_FOUR_FOLDER = "interact_tmp"  # 前台可交互的神经图像参数保存
        VASCULAR_TMP_FIVE_FOLDER = "cnbd_point_tmp"  # cnbd算法运算得到的神经交叉点的临时存储文件夹
        """ ################################# """

        """ ################# 中文映射(出问题难修) ########## """
        CHINESE_FOLDER_DICT = {
            NORMAL_SAVE_PATH: "原图处理结果",
            ENHANCE_SAVE_PATH: "增强图处理结果",
            VASCULAR_BASE_FOLDER: "参数量化结果",

            ORIGIN_ZERO_FOLDER: "原图",
            ORIGIN_ONE_FOLDER: "分割结果",
            ORIGIN_TWO_FOLDER: "分级投影可视化",
            ORIGIN_TWO_SUB_FOLDER: "分级结果及概率",
            ORIGIN_THREE_FOLDER: "分级关注区域可视化",

            VASCULAR_ZERO_FOLDER: "连通域检测可视化",
            VASCULAR_ONE_FOLDER: "中心线提取结果",
            VASCULAR_ONE_SUB_FOLDER: "分支点检测可视化",
            VASCULAR_ONE_THIRD_FOLDER: "分支检测可视化",
            VASCULAR_TWO_FOLDER: "神经方向直方图",
            VASCULAR_THREE_FOLDER: "参数量化结果",

            ORIGIN_NORMAL_TMP: "原图分割结果叠加共焦影像",
            ORIGIN_ENHANCE_TMP: "增强分割结果叠加共焦影像"
        }

        ENG_FOLDER_DICT = {
            NORMAL_SAVE_PATH: "Normal",
            ENHANCE_SAVE_PATH: "Enhanced",
            VASCULAR_BASE_FOLDER: "Measurement",

            ORIGIN_ZERO_FOLDER: "Original Images",
            ORIGIN_ONE_FOLDER: "Segmentation Results",
            ORIGIN_TWO_FOLDER: "TC GradCAMs nerve mapping",
            ORIGIN_TWO_SUB_FOLDER: "TC Grades and Probabilities",
            ORIGIN_THREE_FOLDER: "TC GradCAMs",

            VASCULAR_ZERO_FOLDER: "Connected Components",
            VASCULAR_ONE_FOLDER: "Central Lines",
            VASCULAR_ONE_SUB_FOLDER: "Branch Points",
            VASCULAR_ONE_THIRD_FOLDER: "Branch Analysis",
            VASCULAR_TWO_FOLDER: "Nerve Orientations",
            VASCULAR_THREE_FOLDER: "Nerve Parameters",
        }

        DOWNLOAD_LANGUAGE = {
            "CHI": CHINESE_FOLDER_DICT,
            "ENG": ENG_FOLDER_DICT
        }
        """ ################################# """


        @staticmethod
        def make_dir(path: str):
            if not os.path.exists(path):
                try:
                    os.makedirs(path)
                except Exception as e:
                    print(repr(e))
                    print(traceback.format_exc())
                return False
            return True

        @staticmethod
        def had_upload(path: str):
            files = os.listdir(path)
            if len(files) == 0:
                return False
            return True

    class NetConfig:
        # 神经网络权重文件映射部分，此部分在修改旧代码的时候建立，描述权重文件的相关路径
        """Application.NetConfig
            - -1: 代表这部分代码不执行
            - 其他: 对应的模型路径,如果只是cv算法,也是留空的,除非好几个
        """

        # 图像增强
        ENHANCE_DICT = {
            -1: "",  # 不增强
            0: "weight/StillGAN/checkpoints/isee_csigan/75_net_G_A.pth"
        }

        # 角膜神经分割
        SEGMENTATION_DICT = {
            -1: "",
            0: "weight/csnet/csnet51_484.pth",
            1: "weight/nerveformer/110_Our_FFN(0.8738,0.1813).pth",  # nerveformer
            2: "weight/ceil/csnet.pth",  # csnet.py
            3: "weight/ceil/nerveformer.pth",  # whole_network.py/NF
            4: "weight/nnunet/big/nnUNetPlansv21",  # 注意nnunet框架读取整个文件夹, 这里是大血管模型
            5: "weight/nnunet/small/nnUNetPlansv21",  # 细血管分割模型
            6: "weight/nnunet/faz/nnUNetPlansv21",  # FAZ区域分割模型
            7: "weight/nnunet/intersection/nnUNetPlansv21"  # 动静脉交叉点分割模型, 多分类
        }
        SEGMENTATION_USE_CSNET = 0
        SEGMENTATION_USE_NERVE = 1
        SEGMENTATION_USE_CSNET_CEIL = 2
        SEGMENTATION_USE_NERVE_CEIL = 3
        SEGMENTATION_USE_NNUNET_BIG = 4
        SEGMENTATION_USE_NNUNET_SMALL = 5
        SEGMENTATION_USE_NNUNET_FAZ = 6
        SEGMENTATION_USE_NNUNET_INTERSECTION = 7

        # 弯曲度分级
        # grade实际上也是好几个模型，但是难以看懂逻辑，所以暂时没添加(旧代码比较鬼畜，可以自己看看)
        # 2022.11.22 强制清理旧代码，整合进常量表中
        GRADE_DICT = {
            -1: "",
            0: "weight/DeepGrading/DeepGrading.pth",
            1: "weight/DeepGrading/BANet.pth"
        }

        # 血管量化
        VASCULAR_DICT = {
            -1: "",
            0: ""
        }

    class SysLanguage:
        """用于承担flask系统部分的语言字典职能
        """
        EXCEL_EXPORT_CHI = {
            "FILE_NAME": "文件名",
            "CNFL": "CNFL",
            "CNFD": "CNFD",
            "CTBD": "CTBD",
            "CNBD": "CNBD",
            "TOR_ORG": "原图 弯曲度检测",
            "TOR_EHD": "增强图 弯曲度检测",
            "HIST_ORG": "原图 神经方向角度",
            "HIST_EHD": "增强图 神经方向角度",
            "QUANT_ORG": "原图 血管量化",
            "QUANT_EHD": "增强图 血管量化",
            "PRED_PROB": "弯曲度概率",
            "TOR_GRADE": "弯曲度分级",
            "NER_CHI_CO_ORG": "原图 神经交叉坐标",
            "NER_CHI_CO_EHD": "增强图 神经交叉坐标"
        }
        EXCEL_EXPORT_ENG = {
            "FILE_NAME": "File name",
            "CNFL": "CNFL",
            "CNFD": "CNFD",
            "CTBD": "CTBD",
            "CNBD": "CNBD",
            "TOR_ORG": "Tor Grade(Org)",
            "TOR_EHD": "Tor Grade(Ehd)",
            "HIST_ORG": "Ori Hist(Org)",
            "HIST_EHD": "Ori Hist(Ehd)",
            "QUANT_ORG": "Quant(Org)",
            "QUANT_EHD": "Quant(Ehd)",
            "PRED_PROB": "Pred. Probability",
            "TOR_GRADE": "Tor Grade",
            "NER_CHI_CO_ORG": "Neural chiasmatic coordinates(Org)",
            "NER_CHI_CO_EHD": "Neural chiasmatic coordinates(Ehd)"
        }

        EXCEL_EXPORT_DICT = {
            "CHI": EXCEL_EXPORT_CHI,
            "ENG": EXCEL_EXPORT_ENG
        }

    class WebSocketConfig:
        WS_IP = "127.0.0.1"
        WS_PORT = "40001"  # java端口
        WS_ROUTE = "arcus/flask/monitor/websocket"

